
use super::ChessPoint;
use super::ChessPiece;
use super::ActionResult;
use super::ChessResult;
use super::PieceColor;

use super::Point;

use std::collections::HashMap;

pub const POINT_COUNT: usize = 24;

pub struct Chessboard{
    ///棋盘上黑色棋子数量
    blacks: u8,

    ///棋盘上白色棋子数量
    whites: u8,

    ///棋盘上棋子详情
    points: HashMap<u8, ChessPoint>
}

impl Chessboard{
    pub fn new() -> Chessboard{
        let points = init_piece_map();
        Chessboard{
            blacks: 0,
            whites: 0,
            points
        }
    }

    pub fn board_piece_left(&self, color: &PieceColor) -> u8{
        match *color {
            PieceColor::Black => self.blacks,
            PieceColor::White => self.whites
        }
    }

    ///获取棋盘所有点的棋子情况，包括棋子位置，颜色等。没有棋子的位置颜色为"empty"，走棋阶段吃子位置不可用时，返回"kill"。
    pub fn point_array(&self) -> Vec<Point>{
        let mut vec = Vec::<Point>::with_capacity(POINT_COUNT);
        let points = &self.points;
        for (k, v) in points{
            let color = match v.piece() {
                None => {
                    if v.is_valid(){
                        "empty"
                    }else {
                        "kill"
                    }
                },
                Some(ref p) => {
                    match *p.color() {
                        PieceColor::Black => "black",
                        PieceColor::White => "white"
                    }
                }
            };
            vec.push(Point{index: *k as u32, color: String::from(color)});
        };
        vec
    }

    ///判断两个棋子位置是否相邻
    pub fn is_near(&self, source: &u8, target: u8) -> bool{
        let point = self.points.get(&source).unwrap();
        point.is_near(target)
    }

    ///放一个棋子到棋盘上
    pub fn put(&mut self, index: &u8, piece: ChessPiece) -> ChessResult{
        let color = piece.color().clone();
        let point = self.points.get_mut(index);
        let result = match point {
            None => ChessResult::Error(201, format!("Chessboard error. index = {}", index)),
            Some(p) => p.put_piece(piece)
        };
        if let ChessResult::Ok = result{
            match color {
                PieceColor::Black => self.blacks = self.blacks + 1,
                PieceColor::White => self.whites = self.whites + 1
            }
        };
        result
    }

    ///从棋盘上拿起一个棋子
    pub fn pickup(&mut self, index: &u8, color: &PieceColor) -> Option<ChessPiece>{
        let point = self.points.get_mut(index);
        let piece = match point {
            None => None,
            Some(ref p) => p.pickup(color)
        };
        match piece {
            None => (),
            Some(ref p) => {
                match *color {
                    PieceColor::Black =>  self.blacks = self.blacks - 1,
                    PieceColor::White => self.whites = self.whites - 1
                }
            }
        }
        piece
    }

    ///移除指定位置的棋子
    pub fn remove_piece(&mut self, index: &u8) {
        let point = self.points.get_mut(index).unwrap();
        point.remove_piece();
    }

    ///落子阶段吃子，吃过的位置不能继续放棋子
    pub fn kill_invalid(&mut self, index: &u8, color: &PieceColor) -> ChessResult{
        let point = self.points.get_mut(index);
        let piece = match point {
            None => None,
            Some(ref p) => {
                p.pickup(color)
            }
        };
        match piece{
            None => ChessResult::Error(221, format!("Kill piece fail. Invalid index: {}", index)),
            Some(ref _p) => {
                let po = point.unwrap();
                po.remove_piece();
                po.invalid_point();
                match *color {
                    PieceColor::Black =>  self.blacks = self.blacks - 1,
                    PieceColor::White => self.whites = self.whites - 1
                }
                ChessResult::Ok
            }
        }
    }

    ///走棋阶段吃子，没有吃子位置不能有棋子的限制
    pub fn kill_valid(&mut self, index: &u8, color: &PieceColor) -> ChessResult{
        let point = self.points.get_mut(index);
        let piece = match point {
            None => None,
            Some(ref p) => {
                p.pickup(color)
            }
        };
        match piece{
            None => ChessResult::Error(221, format!("Kill piece fail. Invalid index: {}", index)),
            Some(_p) => {
                point.unwrap().remove_piece();
                match *color {
                    PieceColor::Black =>  self.blacks = self.blacks - 1,
                    PieceColor::White => self.whites = self.whites - 1
                }
                ChessResult::Ok
            }
        }
    }

    ///第一阶段吃子位置会被置为不可用状态，进入第二阶段时要清除掉这些标记。
    pub fn clear_invalid_marker(&mut self){
        self.points.iter_mut().map(|(k, v)|{
            v.clear_marker()
        });
    }

    ///检查是否有可以吃的棋子。如果没有棋子可以吃，则要放弃吃子。
    pub fn have_single_piece(&self, color: &PieceColor) -> bool{
        for (k, p) in &self.points{
            if p.is_piece_color(color){
                if self.check_three(k, color) == 0{
                    return true;
                }
            }
        }
        false
    }

    ///检查成三
    pub fn check_three(&self, index: &u8, color: &PieceColor) -> u8{
        let point = self.points.get(index);
        match point {
            None => {println!("Chessboard error! wrong piece point: {}", index); 0u8},
            Some(ref p) => {
                let mut count = 0u8;
                if !p.is_piece_color(color){
                    return count;
                }
                let horizontal = p.horizontal();
                if self.check_line(horizontal, color){
                    count = count + 1;
                }
                let vertical = p.vertical();
                if self.check_line(vertical, color){
                    count = count + 1;
                }
                count
            }
        }
    }

    ///检查一个方向
    fn check_line(&self, indexes: &[u8; 3], color: &PieceColor) -> bool{
        for i in indexes{
            let point = self.points.get(i);
            let result = match point {
                None => {println!("Chessboard error! index: {}", i); false},
                Some(ref p) => p.is_piece_color(color)
            };
            if !result{
                return result;
            }
        }
        true
    }
}

fn init_piece_map() -> HashMap<u8, ChessPoint>{
    let mut map: HashMap<u8, ChessPoint> = HashMap::with_capacity(POINT_COUNT);
    //第一行
    let p11 = ChessPoint::new(11, [11, 41, 71], [11, 14, 17]);
    let p41 = ChessPoint::new(41, [11, 41, 71], [41, 42, 43]);
    let p71 = ChessPoint::new(71, [11, 41, 71], [71, 74, 77]);

    map.insert(p11.index(), p11);
    map.insert(p41.index(), p41);
    map.insert(p71.index(), p71);

    //第二行
    let p22 = ChessPoint::new(22, [22, 42, 62], [22, 24, 26]);
    let p42 = ChessPoint::new(42, [22, 42, 62], [41, 42, 43]);
    let p62 = ChessPoint::new(62, [22, 42, 62], [62, 64, 66]);

    map.insert(p22.index(), p22);
    map.insert(p42.index(), p42);
    map.insert(p62.index(), p62);

    //第三行
    let p33 = ChessPoint::new(33, [33, 43, 53], [33, 34, 35]);
    let p43 = ChessPoint::new(43, [33, 43, 53], [41, 42, 43]);
    let p53 = ChessPoint::new(53, [33, 43, 53], [53, 54, 55]);

    map.insert(p33.index(), p33);
    map.insert(p43.index(), p43);
    map.insert(p53.index(), p53);

    //第四行左
    let p14 = ChessPoint::new(14, [14, 24, 34], [11, 14, 17]);
    let p24 = ChessPoint::new(24, [14, 24, 34], [22, 24, 26]);
    let p34 = ChessPoint::new(34, [14, 24, 34], [33, 34, 35]);

    map.insert(p14.index(), p14);
    map.insert(p24.index(), p24);
    map.insert(p34.index(), p34);

    //第四行右
    let p54 = ChessPoint::new(54, [54, 64, 74], [53, 54, 55]);
    let p64 = ChessPoint::new(64, [54, 64, 74], [62, 64, 66]);
    let p74 = ChessPoint::new(74, [54, 64, 74], [71, 74, 77]);

    map.insert(p54.index(), p54);
    map.insert(p64.index(), p64);
    map.insert(p74.index(), p74);

    //第五行
    let p35 = ChessPoint::new(35, [35, 45, 55], [33, 34, 35]);
    let p45 = ChessPoint::new(45, [35, 45, 55], [45, 46, 47]);
    let p55 = ChessPoint::new(55, [35, 45, 55], [53, 54, 55]);

    map.insert(p35.index(), p35);
    map.insert(p45.index(), p45);
    map.insert(p55.index(), p55);

    //第六行
    let p26 = ChessPoint::new(26, [26, 46, 66], [22, 24, 26]);
    let p46 = ChessPoint::new(46, [26, 46, 66], [45, 46, 47]);
    let p66 = ChessPoint::new(66, [26, 46, 66], [62, 64, 66]);

    map.insert(p26.index(), p26);
    map.insert(p46.index(), p46);
    map.insert(p66.index(), p66);

    //第七行
    let p17 = ChessPoint::new(17, [17, 47, 77], [11, 14, 17]);
    let p47 = ChessPoint::new(47, [17, 47, 77], [45, 46, 47]);
    let p77 = ChessPoint::new(77, [17, 47, 77], [71, 74, 77]);

    map.insert(p17.index(), p17);
    map.insert(p47.index(), p47);
    map.insert(p77.index(), p77);

    map
}

#[cfg(test)]
mod test{
    use chess::PieceColor;
    use chess::ChessPiece;
    use chess::ActionResult;
    use chess::chessboard::Chessboard;

    #[test]
    pub fn put_piece11(){
        let mut chessboard = &mut Chessboard::new();
        let result = chessboard.put(&11u8, ChessPiece::new(PieceColor::Black));
        assert_eq!(ActionResult::success(), result)
    }

    #[test]
    pub fn put_piece41(){
        let mut chessboard = &mut Chessboard::new();
        let result = chessboard.put(&41u8, ChessPiece::new(PieceColor::Black));
        assert_eq!(ActionResult::success(), result)
    }

    #[test]
    pub fn put_piece71(){
        let mut chessboard = &mut Chessboard::new();
        let result = chessboard.put(&71u8, ChessPiece::new(PieceColor::Black));
        assert_eq!(ActionResult::success(), result)
    }

    #[test]
    pub fn put_piece22(){
        let mut chessboard = &mut Chessboard::new();
        let result = chessboard.put(&22u8, ChessPiece::new(PieceColor::Black));
        assert_eq!(ActionResult::success(), result)
    }

    #[test]
    pub fn put_piece42(){
        let mut chessboard = &mut Chessboard::new();
        let result = chessboard.put(&42u8, ChessPiece::new(PieceColor::Black));
        assert_eq!(ActionResult::success(), result)
    }

    #[test]
    pub fn put_piece62(){
        let mut chessboard = &mut Chessboard::new();
        let result = chessboard.put(&62u8, ChessPiece::new(PieceColor::Black));
        assert_eq!(ActionResult::success(), result)
    }

    #[test]
    pub fn put_piece33(){
        let mut chessboard = &mut Chessboard::new();
        let result = chessboard.put(&33u8, ChessPiece::new(PieceColor::Black));
        assert_eq!(ActionResult::success(), result)
    }

    #[test]
    pub fn put_piece43(){
        let mut chessboard = &mut Chessboard::new();
        let result = chessboard.put(&43u8, ChessPiece::new(PieceColor::Black));
        assert_eq!(ActionResult::success(), result)
    }

    #[test]
    pub fn put_piece53(){
        let mut chessboard = &mut Chessboard::new();
        let result = chessboard.put(&53u8, ChessPiece::new(PieceColor::Black));
        assert_eq!(ActionResult::success(), result)
    }

    #[test]
    pub fn put_piece14(){
        let mut chessboard = &mut Chessboard::new();
        let result = chessboard.put(&14u8, ChessPiece::new(PieceColor::Black));
        assert_eq!(ActionResult::success(), result)
    }

    #[test]
    pub fn put_piece24(){
        let mut chessboard = &mut Chessboard::new();
        let result = chessboard.put(&24u8, ChessPiece::new(PieceColor::Black));
        assert_eq!(ActionResult::success(), result)
    }

    #[test]
    pub fn put_piece34(){
        let mut chessboard = &mut Chessboard::new();
        let result = chessboard.put(&34u8, ChessPiece::new(PieceColor::Black));
        assert_eq!(ActionResult::success(), result)
    }

    #[test]
    pub fn put_piece54(){
        let mut chessboard = &mut Chessboard::new();
        let result = chessboard.put(&54u8, ChessPiece::new(PieceColor::Black));
        assert_eq!(ActionResult::success(), result)
    }

    #[test]
    pub fn put_piece64(){
        let mut chessboard = &mut Chessboard::new();
        let result = chessboard.put(&64u8, ChessPiece::new(PieceColor::Black));
        assert_eq!(ActionResult::success(), result)
    }

    #[test]
    pub fn put_piece74(){
        let mut chessboard = &mut Chessboard::new();
        let result = chessboard.put(&74u8, ChessPiece::new(PieceColor::Black));
        assert_eq!(ActionResult::success(), result)
    }

    #[test]
    pub fn put_piece35(){
        let mut chessboard = &mut Chessboard::new();
        let result = chessboard.put(&35u8, ChessPiece::new(PieceColor::Black));
        assert_eq!(ActionResult::success(), result)
    }

    #[test]
    pub fn put_piece45(){
        let mut chessboard = &mut Chessboard::new();
        let result = chessboard.put(&45u8, ChessPiece::new(PieceColor::Black));
        assert_eq!(ActionResult::success(), result)
    }

    #[test]
    pub fn put_piece55(){
        let mut chessboard = &mut Chessboard::new();
        let result = chessboard.put(&55u8, ChessPiece::new(PieceColor::Black));
        assert_eq!(ActionResult::success(), result)
    }

    #[test]
    pub fn put_piece26(){
        let mut chessboard = &mut Chessboard::new();
        let result = chessboard.put(&26u8, ChessPiece::new(PieceColor::Black));
        assert_eq!(ActionResult::success(), result)
    }

    #[test]
    pub fn put_piece46(){
        let mut chessboard = &mut Chessboard::new();
        let result = chessboard.put(&46u8, ChessPiece::new(PieceColor::Black));
        assert_eq!(ActionResult::success(), result)
    }

    #[test]
    pub fn put_piece66(){
        let mut chessboard = &mut Chessboard::new();
        let result = chessboard.put(&66u8, ChessPiece::new(PieceColor::Black));
        assert_eq!(ActionResult::success(), result)
    }

    #[test]
    pub fn put_piece17(){
        let mut chessboard = &mut Chessboard::new();
        let result = chessboard.put(&17u8, ChessPiece::new(PieceColor::Black));
        assert_eq!(ActionResult::success(), result)
    }

    #[test]
    pub fn put_piece47(){
        let mut chessboard = &mut Chessboard::new();
        let result = chessboard.put(&47u8, ChessPiece::new(PieceColor::Black));
        assert_eq!(ActionResult::success(), result)
    }

    #[test]
    pub fn put_piece77(){
        let mut chessboard = &mut Chessboard::new();
        let result = chessboard.put(&77u8, ChessPiece::new(PieceColor::Black));
        assert_eq!(ActionResult::success(), result)
    }

    #[test]
    pub fn three_twice(){
        let mut chessboard = &mut Chessboard::new();
        let result = chessboard.put(&11u8, ChessPiece::new(PieceColor::Black));
        let result = chessboard.put(&41u8, ChessPiece::new(PieceColor::Black));
        let result = chessboard.put(&71u8, ChessPiece::new(PieceColor::Black));
        let result = chessboard.put(&74u8, ChessPiece::new(PieceColor::Black));
        let result = chessboard.put(&77u8, ChessPiece::new(PieceColor::Black));
        let result = chessboard.check_three(&71u8, &PieceColor::Black);
        assert_eq!(2, result)
    }

    #[test]
    pub fn put_piece_twice(){
        let mut chessboard = &mut Chessboard::new();
        let result = chessboard.put(&11u8, ChessPiece::new(PieceColor::Black));
        let result = chessboard.put(&11u8, ChessPiece::new(PieceColor::Black));
        assert_eq!(false, result.result())
    }

    #[test]
    pub fn put_different_piece(){
        let mut chessboard = &mut Chessboard::new();
        let result = chessboard.put(&11u8, ChessPiece::new(PieceColor::White));
        let result = chessboard.put(&11u8, ChessPiece::new(PieceColor::Black));
        assert_eq!(false, result.result())
    }

    #[test]
    pub fn check_three_11(){
        let mut chessboard = put_all_black();
        let result = chessboard.check_three(&11u8, &PieceColor::Black);
        assert_eq!(2, result)
    }

    #[test]
    pub fn check_three_41(){
        let mut chessboard = put_all_black();
        let result = chessboard.check_three(&41u8, &PieceColor::Black);
        assert_eq!(2, result)
    }

    #[test]
    pub fn check_three_71(){
        let mut chessboard = put_all_black();
        let result = chessboard.check_three(&71u8, &PieceColor::Black);
        assert_eq!(2, result)
    }

    #[test]
    pub fn check_three_22(){
        let mut chessboard = put_all_black();
        let result = chessboard.check_three(&22u8, &PieceColor::Black);
        assert_eq!(2, result)
    }

    #[test]
    pub fn check_three_42(){
        let mut chessboard = put_all_black();
        let result = chessboard.check_three(&42u8, &PieceColor::Black);
        assert_eq!(2, result)
    }

    #[test]
    pub fn check_three_62(){
        let mut chessboard = put_all_black();
        let result = chessboard.check_three(&62u8, &PieceColor::Black);
        assert_eq!(2, result)
    }

    #[test]
    pub fn check_three_33(){
        let mut chessboard = put_all_black();
        let result = chessboard.check_three(&33u8, &PieceColor::Black);
        assert_eq!(2, result)
    }

    #[test]
    pub fn check_three_43(){
        let mut chessboard = put_all_black();
        let result = chessboard.check_three(&43u8, &PieceColor::Black);
        assert_eq!(2, result)
    }

    #[test]
    pub fn check_three_53(){
        let mut chessboard = put_all_black();
        let result = chessboard.check_three(&53u8, &PieceColor::Black);
        assert_eq!(2, result)
    }

    #[test]
    pub fn check_three_14(){
        let mut chessboard = put_all_black();
        let result = chessboard.check_three(&14u8, &PieceColor::Black);
        assert_eq!(2, result)
    }

    #[test]
    pub fn check_three_24(){
        let mut chessboard = put_all_black();
        let result = chessboard.check_three(&24u8, &PieceColor::Black);
        assert_eq!(2, result)
    }

    #[test]
    pub fn check_three_34(){
        let mut chessboard = put_all_black();
        let result = chessboard.check_three(&34u8, &PieceColor::Black);
        assert_eq!(2, result)
    }

    #[test]
    pub fn check_three_54(){
        let mut chessboard = put_all_black();
        let result = chessboard.check_three(&54u8, &PieceColor::Black);
        assert_eq!(2, result)
    }

    #[test]
    pub fn check_three_64(){
        let mut chessboard = put_all_black();
        let result = chessboard.check_three(&64u8, &PieceColor::Black);
        assert_eq!(2, result)
    }

    #[test]
    pub fn check_three_74(){
        let mut chessboard = put_all_black();
        let result = chessboard.check_three(&74u8, &PieceColor::Black);
        assert_eq!(2, result)
    }

    #[test]
    pub fn check_three_35(){
        let mut chessboard = put_all_black();
        let result = chessboard.check_three(&35u8, &PieceColor::Black);
        assert_eq!(2, result)
    }

    #[test]
    pub fn check_three_45(){
        let mut chessboard = put_all_black();
        let result = chessboard.check_three(&45u8, &PieceColor::Black);
        assert_eq!(2, result)
    }

    #[test]
    pub fn check_three_55(){
        let mut chessboard = put_all_black();
        let result = chessboard.check_three(&55u8, &PieceColor::Black);
        assert_eq!(2, result)
    }

    #[test]
    pub fn check_three_26(){
        let mut chessboard = put_all_black();
        let result = chessboard.check_three(&26u8, &PieceColor::Black);
        assert_eq!(2, result)
    }

    #[test]
    pub fn check_three_46(){
        let mut chessboard = put_all_black();
        let result = chessboard.check_three(&46u8, &PieceColor::Black);
        assert_eq!(2, result)
    }

    #[test]
    pub fn check_three_66(){
        let mut chessboard = put_all_black();
        let result = chessboard.check_three(&66u8, &PieceColor::Black);
        assert_eq!(2, result)
    }

    #[test]
    pub fn check_three_17(){
        let mut chessboard = put_all_black();
        let result = chessboard.check_three(&17u8, &PieceColor::Black);
        assert_eq!(2, result)
    }

    #[test]
    pub fn check_three_47(){
        let mut chessboard = put_all_black();
        let result = chessboard.check_three(&47u8, &PieceColor::Black);
        assert_eq!(2, result)
    }

    #[test]
    pub fn check_three_77(){
        let mut chessboard = put_all_black();
        let result = chessboard.check_three(&77u8, &PieceColor::Black);
        assert_eq!(2, result)
    }

    #[test]
    pub fn pick_all(){
        let mut chessboard = &mut put_all_black();
        let result = chessboard.pickup(&55u8, &PieceColor::Black);
        assert_ne!(None, result)
    }

    fn put_all_black() -> Chessboard {
        let mut chessboard = Chessboard::new();
        let result = chessboard.put(&11u8, ChessPiece::new(PieceColor::Black));
        let result = chessboard.put(&41u8, ChessPiece::new(PieceColor::Black));
        let result = chessboard.put(&71u8, ChessPiece::new(PieceColor::Black));
        let result = chessboard.put(&22u8, ChessPiece::new(PieceColor::Black));
        let result = chessboard.put(&42u8, ChessPiece::new(PieceColor::Black));
        let result = chessboard.put(&62u8, ChessPiece::new(PieceColor::Black));
        let result = chessboard.put(&33u8, ChessPiece::new(PieceColor::Black));
        let result = chessboard.put(&43u8, ChessPiece::new(PieceColor::Black));
        let result = chessboard.put(&53u8, ChessPiece::new(PieceColor::Black));
        let result = chessboard.put(&14u8, ChessPiece::new(PieceColor::Black));
        let result = chessboard.put(&24u8, ChessPiece::new(PieceColor::Black));
        let result = chessboard.put(&34u8, ChessPiece::new(PieceColor::Black));
        let result = chessboard.put(&54u8, ChessPiece::new(PieceColor::Black));
        let result = chessboard.put(&64u8, ChessPiece::new(PieceColor::Black));
        let result = chessboard.put(&74u8, ChessPiece::new(PieceColor::Black));
        let result = chessboard.put(&35u8, ChessPiece::new(PieceColor::Black));
        let result = chessboard.put(&45u8, ChessPiece::new(PieceColor::Black));
        let result = chessboard.put(&55u8, ChessPiece::new(PieceColor::Black));
        let result = chessboard.put(&26u8, ChessPiece::new(PieceColor::Black));
        let result = chessboard.put(&46u8, ChessPiece::new(PieceColor::Black));
        let result = chessboard.put(&66u8, ChessPiece::new(PieceColor::Black));
        let result = chessboard.put(&17u8, ChessPiece::new(PieceColor::Black));
        let result = chessboard.put(&47u8, ChessPiece::new(PieceColor::Black));
        let result = chessboard.put(&77u8, ChessPiece::new(PieceColor::Black));
        chessboard
    }
}